﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Afterlife.Entities;
using AfterlifeLib;
using AfterlifeLib.Input;
using AfterlifeLib.Levels;
using AfterlifeLib.Lighting;
using AfterlifeLib.Particles;
using AfterlifeLib.Projectiles;
using AfterlifeLib.Weapons;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Afterlife
{
    public class AfterlifeWorld : World
    {
        PrimitiveBatch PrimitiveBatch;

        ProjectileManager projectileManager = new ProjectileManager();
        public ProjectileManager ProjectileManager
        {
            get { return projectileManager; }
        }

        ParticleManager particleManager = new ParticleManager();
        public ParticleManager ParticleManager
        {
            get { return particleManager; }
        }
            
        Dictionary<string, Gun> loadedWeapons = new Dictionary<string, Gun>();
        public Dictionary<string, Gun> LoadedWeapons
        {
            get { return loadedWeapons; }
        }

        HeadsUpDisplay hud = new HeadsUpDisplay();

        SpriteFont debugFont;
        SpriteFont HUDFont;
        Effect lightEffect;
        Effect combinedEffect;

        RenderTarget2D colorMapRenderTarget;
        RenderTarget2D shadowMapRenderTarget;
        Texture2D colorMapTexture;
        Texture2D shadowMapTexture;

        VertexDeclaration vertexDeclaration;
        VertexPositionTexture[] vertices = new VertexPositionTexture[]
        {
            new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)),
            new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(1, 0)),
            new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 1)),
            new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1, 1)),
        };

        int activeLights = 0;

        Level currentLevel;
        public Level CurrentLevel
        {
            get { return currentLevel; }
        }

        Player player;

        List<Entity> entities = new List<Entity>();

        List<Light> otherLights = new List<Light>();

        #region Debug Variables
        bool lightingEnabled = true;
        #endregion

        public AfterlifeWorld(ContentManager content, GraphicsDevice GraphicsDevice, SpriteBatch SpriteBatch) : base(content, GraphicsDevice, SpriteBatch)
        {
            this.PrimitiveBatch = new PrimitiveBatch(GraphicsDevice);
        }

        public override void Initialize()
        {
            ParticleManager.AddProjectileDestroyedEvent(ProjectileManager);

            Level.TileHeight = 64;
            Level.TileWidth = 64;
            Level.TileDrawWidth = 64;
            Level.TileDrawHeight = 64;

            ProjectileManager.Initialize(this);
            ParticleManager.Initialize();

            player = new Player(this);
            player.Initialize(new Vector2(128, 128), 125f);

            #region RenderTargets
            PresentationParameters pp = GraphicsDevice.PresentationParameters;
            int width = pp.BackBufferWidth;
            int height = pp.BackBufferHeight;
            SurfaceFormat format = pp.BackBufferFormat;
            colorMapRenderTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, format);
            shadowMapRenderTarget = new RenderTarget2D(GraphicsDevice, width, height, 1, format);
            #endregion

            vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);
        }

        public override void LoadContent()
        {
            //SpriteFonts
            debugFont = Content.Load<SpriteFont>("Fonts/arial10");
            HUDFont = Content.Load<SpriteFont>("Fonts/arial14");

            //Effects
            lightEffect = Content.Load<Effect>("HLSL/LightingShadow");
            combinedEffect = Content.Load<Effect>("HLSL/LightingCombined");

            //Levels
            currentLevel = Content.Load<Level>("Levels/houses");

            //Weapons
            loadedWeapons.Add("Colt_M4_Carbine", Content.Load<Gun>("Weapons/Colt_M4_Carbine"));

            //ParticleSettings
            ParticleManager.AddParticleSettings(Content, "ParticleSettings/Dust");

            //HUD
            hud.LoadContent(Content);

            player.LoadContent(Content);

            entities.Add(new Zombie(this, new Vector2(100, 100)));
            entities.Add(new Zombie(this, new Vector2(300, 200)));
            entities.Add(new Zombie(this, new Vector2(600, 400)));
            entities.Add(new Zombie(this, new Vector2(300, 700)));
            foreach (Entity e in entities)
                e.LoadContent(Content);
        }

        public override void UnloadContent()
        {
            Content.Unload();
        }

        public override void Update(GameTime gameTime)
        {
            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

            ProjectileManager.Update(gameTime, CurrentLevel);
            ParticleManager.Update(gameTime);

            player.Update(gameTime);

            foreach (Entity e in entities)
            {
                if (e.GetType() == typeof(Zombie))
                {
                    Zombie z = (Zombie)e;
                    z.Target = player.Center;
                }

                e.Update(gameTime);
            }

#if DEBUG
            UpdateDebug();
#endif
        }

        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.SetRenderTarget(0, colorMapRenderTarget);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1, 0);

            foreach (var layer in CurrentLevel.TileLayers)
            {
                layer.Draw(SpriteBatch, player.Camera);
            }

            SpriteBatch.Begin();
            {
                foreach (Entity e in entities)
                {
                    e.Draw(SpriteBatch, player.Camera);
                }
            }
            SpriteBatch.End();

            player.Draw(SpriteBatch, player.Camera);

            GraphicsDevice.SetRenderTarget(0, null);

            colorMapTexture = colorMapRenderTarget.GetTexture();
            shadowMapTexture = GetShadowMap();

            DrawCombinedMaps();
        }

        private Texture2D GetShadowMap()
        {
            GraphicsDevice.SetRenderTarget(0, shadowMapRenderTarget);
            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1, 0);

            GraphicsDevice.RenderState.AlphaBlendEnable = true;
            GraphicsDevice.RenderState.SourceBlend = Blend.One;
            GraphicsDevice.RenderState.DestinationBlend = Blend.One;

            Camera2D camera = player.Camera;

            activeLights = 0;

            if (lightingEnabled)
                foreach (var light in CurrentLevel.Lights)
                {
                    DrawLight(camera, light);
                }

            //TODO: Automatically add lights from the player and weapons to a seperate List of Lights.
            DrawLight(camera, player.FlashLight);
            if(player.Weapon.ShootLight.IsEnabled)
                DrawLight(camera, player.Weapon.ShootLight);

            GraphicsDevice.RenderState.AlphaBlendEnable = false;

            GraphicsDevice.SetRenderTarget(0, null);

            return shadowMapRenderTarget.GetTexture();
        }

        private void DrawLight(Camera2D camera, Light light)
        {
            if (!light.IsEnabled ||
                light.Position.X + light.Radius < camera.Position.X ||
                light.Position.Y + light.Radius < camera.Position.Y ||
                light.Position.X - light.Radius > camera.Position.X + Camera2D.ScreenWidth ||
                light.Position.Y - light.Radius > camera.Position.Y + Camera2D.ScreenHeight)
                return;

            lightEffect.CurrentTechnique = lightEffect.Techniques["PointLight"];
            lightEffect.Parameters["screenWidth"].SetValue(GraphicsDevice.Viewport.Width);
            lightEffect.Parameters["screenHeight"].SetValue(GraphicsDevice.Viewport.Height);

            lightEffect.Parameters["lightStrength"].SetValue(light.Strength);
            lightEffect.Parameters["lightRadius"].SetValue(light.Radius);
            lightEffect.Parameters["lightPosition"].SetValue(light.GetScreenPosition(player.Camera));
            lightEffect.Parameters["lightColor"].SetValue(light.ColorV3);

            //lightEffect.Parameters["NormalMap"].SetValue();
            //lightEffect.Parameters["DepthMap"].SetValue();

            lightEffect.Begin();
            {
                foreach (var pass in lightEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    {
                        GraphicsDevice.VertexDeclaration = vertexDeclaration;
                        GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, vertices, 0, 2);
                    }
                    pass.End();
                }
            }
            lightEffect.End();

            activeLights++;
        }

        private void DrawCombinedMaps()
        {
            combinedEffect.CurrentTechnique = combinedEffect.Techniques["LightingCombined"];
            combinedEffect.Parameters["ambient"].SetValue(CurrentLevel.AmbientAlpha);
            combinedEffect.Parameters["ambientColor"].SetValue(lightingEnabled ? CurrentLevel.AmbientColor.ToVector4() : Color.White.ToVector4());
            combinedEffect.Parameters["lightAmbient"].SetValue(Light.ambient);

            combinedEffect.Parameters["ColorMap"].SetValue(colorMapTexture);
            combinedEffect.Parameters["ShadowMap"].SetValue(shadowMapTexture);

            SpriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
            {
                combinedEffect.Begin();
                {
                    foreach (var pass in combinedEffect.CurrentTechnique.Passes)
                    {
                        pass.Begin();
                        {
                            SpriteBatch.Draw(colorMapTexture, Vector2.Zero, Color.White);
                        }
                        pass.End();
                    }
                }
                combinedEffect.End();
            }
            SpriteBatch.End();

            DrawAfterLighting();
        }

        private void DrawAfterLighting()
        {
            ProjectileManager.Draw(PrimitiveBatch, player.Camera);
            ParticleManager.Draw(SpriteBatch, player.Camera);

            hud.Draw(SpriteBatch, new HeadsUpDisplayData(player, player.Weapon));

#if DEBUG
            DrawDebug();
#endif
        }

        private void UpdateDebug()
        {
            if (InputState.IsKeyFirstDown(Keys.L))
                lightingEnabled = !lightingEnabled;
            if(InputState.IsKeyDown(Keys.Z))
                Light.ambient -= 0.01f;
            if(InputState.IsKeyDown(Keys.X))
                Light.ambient += 0.01f;
        }

        private void DrawDebug()
        {
            int startX = 30;
            int startY = 60;
            int incrementY = 15;

            List<string> debugLines = new List<string>();
            debugLines.Add(string.Format("Camera Position: {0}", player.Camera.Position));
            debugLines.Add(string.Format("Player Position: {0}", player.Position));
            debugLines.Add("");
            debugLines.Add(string.Format("Ambient Color: {0}", CurrentLevel.AmbientColor));
            debugLines.Add(string.Format("Ambient Alpha: {0}", CurrentLevel.AmbientColor.A));
            debugLines.Add(string.Format("Light Ambient: {0}", Light.ambient));
            debugLines.Add(string.Format("Total Lights: {0}", CurrentLevel.Lights.Count));
            debugLines.Add(string.Format("Active Lights: {0}", activeLights));
            debugLines.Add("");
            debugLines.Add(string.Format("Projectiles: Active: {0}, Inactive: {1}", ProjectileManager.ActiveProjectiles, ProjectileManager.InactiveProjectiles));
            debugLines.Add(string.Format("Particles: Active: {0}, Inactive: {1}", ParticleManager.ActiveProjectiles, ParticleManager.InactiveProjectiles));
            debugLines.Add("");
            debugLines.Add(player.Weapon.ToString());
            debugLines.Add("");
            debugLines.Add("Toggle Lighting Key: L");

            SpriteBatch.Begin();
            foreach (string debugLine in debugLines)
            {
                SpriteBatch.DrawString(debugFont, debugLine, new Vector2(startX, startY), Color.White);
                startY += incrementY;
            }
            SpriteBatch.End();
        }
    }
}
